refactor: add IAsyncDisposable to async-managing types#139
Merged
mivertowski merged 1 commit intomainfrom Apr 21, 2026
Merged
Conversation
Upgrades 16 types that manage async resources (background tasks,
semaphores, channels, CTS, GPU handles) to support cooperative async
disposal. Callers in async contexts now have a clean shutdown path
that does not block on `.GetAwaiter().GetResult()` or `Task.Wait()`.
Each upgraded type gets a `DisposeAsync` that awaits pending work
before releasing resources, while its sync `Dispose` is preserved
for `using` blocks and non-async callers.
Types upgraded (by heuristic criteria):
Core:
- LogBuffer (sync-over-async + Channel drain)
- StructuredLogger (sync-over-async on log buffer flush)
- MemorySanitizer (sync-over-async on secure wipe)
- MemoryProtection (sync-over-async on secure wipe)
LINQ:
- BackpressureManager (CTS + producer unblock)
- RuntimeExecutor (AsTask().Wait() on accelerator dispose)
- CudaRuntimeKernelCompiler (SemaphoreSlim.Wait() in Dispose)
- MetalRuntimeKernelCompiler(SemaphoreSlim.Wait() in Dispose)
- CompilationPipeline (cascades GPU compiler disposal)
CUDA:
- CudaPerformanceProfiler (sync-over-async on StopProfilingAsync)
- CudaPersistentKernelManager (sync-over-async on StopKernelAsync)
- IPersistentKernelHandle (interface extended with IAsyncDisposable)
- PersistentKernelHandle (awaits StopAsync)
Metal:
- MetalBackend (AsTask().GetAwaiter().GetResult() on
accelerator.DisposeAsync)
Plugins/Security:
- NuGetPluginManager (sync-over-async on UnloadPluginAsync)
- SandboxedPlugin (sync-over-async on TerminateAsync)
- PluginSandbox (sync-over-async on plugin termination)
Algorithms:
- KernelSandbox (Task.WaitAll(30s) on sandbox destroy)
Deliberately skipped:
- PipelineTelemetryCollector, NumaScheduler, MetalGraphExecutor,
MetalEventManager, CudaEventManager, MultiGpuSynchronizer,
ParallelOptimizations.WorkStealingPool, PluginLifecycleManager,
AlgorithmPluginLifecycle — their Dispose is purely synchronous
(CTS + Timer, thread.Join, or trivial handle release) with no
awaitable work to drain.
- CudaMemoryManager, MetalMemoryManager — already inherit
IAsyncDisposable from BaseMemoryManager.
- CudaRingKernelRuntime — already implements IAsyncDisposable via
IRingKernelRuntime.
Test call-site migrations (sync `Dispose()` inside async test
methods → `await ...DisposeAsync()`): 4 test methods across
MemorySanitizerTests, MemoryProtectionTests, and PluginSandboxTests.
Build: 0 errors, 0 warnings (Release).
Tests: all affected unit suites pass (Core 486/486, Plugins Sandbox
14/14, Core Security 101/101). Two pre-existing flaky failures on
main (`RingKernelWatchdogTests.Dispose_CleansUpResources`,
`TimestampInjectorTests.InjectTimestampIntoPtx_...`) are unaffected.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Upgrades 16 types that manage async resources (background tasks, semaphores, channels, CTS, GPU handles) to implement
IAsyncDisposablealongsideIDisposable. Each now has a cooperative async shutdown path that awaits in-flight work instead of blocking on.GetAwaiter().GetResult()orTask.Wait().dotnet build DotCompute.sln -c Release).main(RingKernelWatchdogTests.Dispose_CleansUpResources,TimestampInjectorTests.InjectTimestampIntoPtx_InjectedCode_MaintainsValidPtxSyntax) are unaffected.Types upgraded (with heuristic trigger)
BackpressureManagersrc/Extensions/DotCompute.Linq/Reactive/BackpressureManager.csLogBuffersrc/Core/DotCompute.Core/Logging/LogBuffer.csStructuredLoggersrc/Core/DotCompute.Core/Logging/StructuredLogger.csMemorySanitizersrc/Core/DotCompute.Core/Security/MemorySanitizer.csMemoryProtectionsrc/Core/DotCompute.Core/Security/MemoryProtection.csCudaPerformanceProfilersrc/Backends/DotCompute.Backends.CUDA/Profiling/CudaPerformanceProfiler.csStopProfilingAsyncCudaPersistentKernelManagersrc/Backends/DotCompute.Backends.CUDA/Persistent/CudaPersistentKernelManager.csStopKernelAsyncfor each active kernelIPersistentKernelHandle/PersistentKernelHandleStopAsync; interface extendedMetalBackendsrc/Backends/DotCompute.Backends.Metal/MetalBackend.csAsTask().GetAwaiter().GetResult()on each acceleratorRuntimeExecutorsrc/Extensions/DotCompute.Linq/CodeGeneration/RuntimeExecutor.csAsTask().Wait()on accelerator disposeCudaRuntimeKernelCompilersrc/Extensions/DotCompute.Linq/Compilation/CudaRuntimeKernelCompiler.csSemaphoreSlim.Wait()in sync DisposeMetalRuntimeKernelCompilersrc/Extensions/DotCompute.Linq/Compilation/MetalRuntimeKernelCompiler.csSemaphoreSlim.Wait()in sync DisposeCompilationPipelinesrc/Extensions/DotCompute.Linq/CodeGeneration/CompilationPipeline.csNuGetPluginManagersrc/Runtime/DotCompute.Plugins/Managers/NuGetPluginManager.csUnloadPluginAsyncSandboxedPluginsrc/Runtime/DotCompute.Plugins/Security/SandboxedPlugin.csTerminateAsyncPluginSandboxsrc/Runtime/DotCompute.Plugins/Security/PluginSandbox.csKernelSandboxsrc/Extensions/DotCompute.Algorithms/Security/KernelSandbox.csTask.WaitAll(30s)on sandbox destruction tasksDeliberately skipped
PipelineTelemetryCollector,NumaScheduler,MetalGraphExecutor,MetalEventManager,CudaEventManager,MultiGpuSynchronizer,ParallelOptimizations.WorkStealingPool,PluginLifecycleManager,AlgorithmPluginLifecycle— sync-onlyDispose(CTS cancel + timer dispose,Thread.Join, or trivial handle release). No awaitable in-flight work to drain; over-async would be churn.CudaMemoryManager,MetalMemoryManager— already implementIAsyncDisposableviaBaseMemoryManager.CudaRingKernelRuntime— already implementsIAsyncDisposableviaIRingKernelRuntime.Call-site migrations
4 test methods that called sync
Dispose()insideasync Taskmethods were upgraded toawait ...DisposeAsync():tests/Unit/DotCompute.Core.Tests/Security/MemorySanitizerTests.cs— 2 casestests/Unit/DotCompute.Core.Tests/Security/MemoryProtectionTests.cs— 1 casetests/Unit/DotCompute.Plugins.Tests/Security/PluginSandboxTests.cs— 2 casesProduction call sites were left as-is: the build-time CA1849/VSTHRD103 analyzers would have flagged any regression (and the build is warning-clean).
Pattern applied
Each upgraded class keeps both sync and async paths idempotent and shares cleanup where reasonable:
No new interfaces were introduced — all additions go through
System.IAsyncDisposable.Test plan
dotnet build DotCompute.sln -c Release→ 0 errors, 0 warningsdotnet testonDotCompute.Core.Tests(486/486),DotCompute.Backends.CPU.Tests(25/25),DotCompute.Abstractions.Tests(93/93),DotCompute.Plugins.TestsfilterPluginSandboxTests(14/14),DotCompute.Core.TestsfilterMemory{Sanitizer,Protection}Tests(101/101)main— no new test failures introduced by this PRusingblocks on these types should migrate toawait using🤖 Generated with Claude Code